Skip to content

docs(agents): state the hidden-tests rule as the invariant check:type-check-coverage enforces - #15340

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14942-agents-hidden-tests-rule-wording
Sep 4, 2026
Merged

docs(agents): state the hidden-tests rule as the invariant check:type-check-coverage enforces#15340
os-zhuang merged 1 commit into
mainfrom
claude/issue-14942-agents-hidden-tests-rule-wording

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #14942

AGENTS.md's hidden-tests bullet was exclude-shaped, but the gate that enforces it is
not. check:type-check-coverage decides the question per file and spelling-agnostically
— a test file named by an exclude or never reached by any include is hidden from the
typecheck just the same. So the prose declared a narrower rule than the tree enforces: an
author who obeyed AGENTS.md to the letter could still hide a whole test tree by never
including it, and would then be stopped by a rule they were never told.

This is the declared-vs-enforced shape pointed the documentation way. The enforcement is
right and already spelling-agnostic; only the sentence was short. No new gate is added.

The change

One bullet in AGENTS.md (the first of the three under "Three principles the ratchet's
invariants encode"). Before:

  • Never exclude *.test.ts / *.spec.ts from a package's tsconfig.json
    tsc --noEmit reads that config, so the exclusion hides the tests from the very
    check the typecheck script advertises (a green gate over source nothing read). When
    the build config must keep the exclusion, add a sibling tsconfig.test.json and name
    it in the typecheck script (the packages/spec pattern); the sibling may carry its
    own module semantics to match vitest, never its own strictness.

After:

  • A package's tsconfig.json reaches every *.test.ts / *.spec.ts in it
    tsc --noEmit reads that config, so a test it misses is hidden from the very check
    the typecheck script advertises (a green gate over source nothing read).
    check:type-check-coverage fails both spellings per file: an exclude naming it, an
    include that never does. A sibling tsconfig.test.json named in the typecheck
    script may carry its own module semantics for vitest, never its own strictness.

The lead is now the invariant rather than one spelling of its violation, both breaking
spellings are named, and the enforcing instrument is named. The tsc --noEmit reason clause
and the "a green gate over source nothing read" phrase are kept.

The line budget, measured

AGENTS.md sits at 1161 lines against a 1162 ceiling (check:pm-skill-ratchet), and a
sibling flight may need that last line. The rewrite is in place: the bullet occupies the
same 6 lines (75-80) at the same wrap width — widest line 87 bytes before, 87 after,
against the gate's 120-byte cap. No re-wrap bought room, nothing outside the bullet moved.

base 2ed6be64  ✓ check-skill-line-ratchet: AGENTS.md is 1161 lines (ceiling 1162; headroom 1).
HEAD 392aa366  ✓ check-skill-line-ratchet: AGENTS.md is 1161 lines (ceiling 1162; headroom 1).
git diff --stat: AGENTS.md | 12 +++---, 1 file changed, 6 insertions(+), 6 deletions(-)

Two clauses were compressed to pay for the two added ones, both recoverable from the gate the
bullet now names — its failure text carries them verbatim: the condition on the sibling route
("when the build config must keep the exclusion") and the packages/spec worked-example
pointer. The operationally load-bearing half, named in the typecheck script, is kept.

Verification

Gate set re-derived from the real change set at HEAD (node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, 14 commands) and reconciled against the
dispatched list — identical, nothing added. Every exit code captured before any pipe. All 14
green:

EXIT=0 node scripts/check-closing-keyword-parity.mjs
EXIT=0 node scripts/check-closing-keyword-parity.mjs --self-test
EXIT=0 node scripts/check-comment-mask-corpus.mjs
EXIT=0 node scripts/check-required-contexts.mjs --verify-required-set   (NODE_OPTIONS=--use-env-proxy)
EXIT=0 pnpm check:agent-test-spelling
EXIT=0 pnpm check:docs-audit-scope
EXIT=0 pnpm check:nul-bytes
EXIT=0 pnpm check:pm-governed-merges
EXIT=0 pnpm check:pm-governed-prose
EXIT=0 pnpm check:pm-skill-id-lint
EXIT=0 pnpm check:pm-skill-ratchet
EXIT=0 pnpm check:refd-timer-probe
EXIT=0 pnpm check:required-contexts
EXIT=0 pnpm check:watch-hint-literal

Landing regime, asserted rather than assumed:

node scripts/pm/check-governed-merges.mjs --test AGENTS.md   -> EXIT=3
  ⛔  GOVERNED — a human merge is the review record for this PR (#9495 regime).
      AGENTS.md ×1 — the repo-root agent instruction file

The gate the prose now names, run to confirm it is real and green, and that it counts per file:

pnpm check:type-check-coverage -> EXIT=0
  test layer: 10 package(s) still hide their own tests from tsc (146 files hidden as counted
  by this run, 100 frozen raw errors in TEST_DEBT).

Control-byte self-scan beyond check:nul-bytes: grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' AGENTS.md -> no match (exit 1).

Whole-repo pnpm lint: narrowed, and the narrowing is measured

Declared narrowing rather than a whole-repo scan, with all three pieces of evidence:

  1. Population, read from eslint's own config (not from a guess): every files: entry in
    eslint.config.mjs names only {ts,tsx,mts,cts,js,jsx,mjs,cjs}. No markdown glob, no
    markdown processor.
  2. Count, read from --format json: eslint's own verdict on the single changed path is
    one result entry carrying "message": "File ignored because no matching configuration was supplied.", errorCount: 00 files of this diff are in the lint population.
  3. Invariance for untouched files: type-aware linting is not enabled (eslint.config.mjs:328
    states it outright — no parserOptions.project, no typed rules; every parserOptions in
    the file carries ecmaVersion/sourceType only), so a markdown edit in no tsc program can
    move no verdict on any file it does not touch.

CI runs the whole farm regardless.

Landing

Governed surface, so this stays a draft: no seat flips it ready, enqueues it, arms
auto-merge, or requests review. Labelled skip-changesetAGENTS.md publishes nothing from
any package.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…nforces

`AGENTS.md`'s hidden-tests bullet was exclude-shaped, but
`check:type-check-coverage` enforces the invariant per file and
spelling-agnostically: a test named by an `exclude` OR never reached by any
`include` is hidden from the typecheck. The prose declared a narrower rule than
the tree enforces, so an author who obeyed it to the letter could still hide a
whole test tree by never including it.

Restate the bullet as the invariant, name both spellings that break it, and name
`check:type-check-coverage` as the enforcing instrument. Rewritten inside its
existing 6 lines at the existing wrap width (widest line 87B before and after),
so the ratcheted file line count is unchanged at 1161.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants